package com.cyou.servlet;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.log4j.Logger;
import org.json.JSONException;
import org.json.JSONObject;

import com.cyou.util.Constant;
import com.cyou.util.MD5;
import com.cyou.util.UUIDHexGenerator;

/**
 * Servlet implementation class FileUploadServlet
 */
public class FileUploadServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	private static String webBasePath;
	private static List<String> imageTypeList = new ArrayList<String>();
	private static List<String> fileTypeList = new ArrayList<String>();
	private static ResourceBundle rb;

	static {
		if (rb == null) {
			rb = ResourceBundle.getBundle("qbpic_config");
			webBasePath = rb.getString("web.path.base");
			String imageTypes = rb.getString("file.image.type");
			String[] imageType = imageTypes.split(",");
			for (String image : imageType) {
				imageTypeList.add(image);
			}
			String textTypes = rb.getString("file.text.type");
			String[] textType = textTypes.split(",");
			for (String text : textType) {
				fileTypeList.add(text);
			}
		}
	}

	/**
	 * 接受文件上传并存储文件
	 * 先存入缓存文件目录 再移入正式文件目录
	 * @author zoudongchao
	 * @param request
	 * @param response
	 * @throws ServletException IOException
	 */
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

		Logger logger = Logger.getLogger(FileUploadServlet.class);

		String ret = "";
		List<FileItem> fileItems = new ArrayList<FileItem>();
		Map<String, String> formFieldMap = new HashMap<String, String>();
		List<File> fileList = new ArrayList<File>();
		List<String> filePathList = new ArrayList<String>();
		JSONObject urlObj = new JSONObject();

		//存入缓存目录,返回新文件名列表、文件列表、文件存储路径列表和请求中的其他参数
		ret = uploadToTempServer(request, response, fileItems, formFieldMap, filePathList, fileList);

		try {
			if (!Constant.FINISH.equals(ret)) {
				urlObj.append("error", ret);
				logger.debug("error:" + ret);
			} else if (fileList != null && fileList.size() > 0) {
				for (int i = 0; i < fileList.size(); i++) {
					String[] tmp = moveFileToPath(request, (String) filePathList.get(i), (File) fileList.get(i),
							formFieldMap);
					urlObj.append(tmp[0], tmp[1]);
					logger.debug("[doPost]file link" + tmp[1]);
				}
			}
		} catch (JSONException e) {
			logger.error("[doPost] failed to append to json " + e);
		}

		response.setStatus(HttpStatus.SC_OK);// 200
		response.setContentType("application/json;charset=UTF-8");
		PrintWriter out = response.getWriter();
		out.print(urlObj);
		out.flush();
		out.close();
	}

	/**
	 * 上传文件至缓存目录
	 * 获取非文件域（request传参等）
	 * 验证文件格式 大小
	 * 设定文件名称 文件部分存储目录
	 * @param request
	 * @param response
	 * @param fileItems
	 * @param formFieldMap
	 * @param filePathList
	 * @param fileList
	 * @return 
	 */
	@SuppressWarnings("unchecked")
	private String uploadToTempServer(HttpServletRequest request, HttpServletResponse response,
			List<FileItem> fileItems, Map<String, String> formFieldMap, List<String> filePathList, List<File> fileList) {

		Logger logger = Logger.getLogger(FileUploadServlet.class);

		String fileSeparator = File.separator;

		String tempDirStr = System.getProperty("java.io.tmpdir") + fileSeparator + "tianlang";
		logger.debug("[uploadToTempServer] The temp file dir is " + tempDirStr);

		//设定缓存目录
		File tempDir = new File(tempDirStr);
		if (!tempDir.isDirectory()) {
			logger.debug("[uploadToTempServer] dir does not exits, making...");
			tempDir.mkdirs();
		}

		try {
			DiskFileItemFactory factory = new DiskFileItemFactory();
			factory.setSizeThreshold(Integer.parseInt(rb.getString("request.cache")));
			factory.setRepository(tempDir);

			ServletFileUpload upload = new ServletFileUpload(factory);
			upload.setSizeMax(Long.parseLong(rb.getString("request.size.max")));

			fileItems = upload.parseRequest(request);

			//获取非文件类型域  如外部传入的文件名等等
			Iterator<FileItem> paramIter = fileItems.iterator();
			while (paramIter.hasNext()) {
				FileItem fi = (FileItem) paramIter.next();
				if (fi.isFormField()) {//ֵ
					formFieldMap.put(fi.getFieldName(), fi.getString());
				}
			}
			
			//获取文件大小限制，不允许空
			String maxSize = formFieldMap.get("maxSize");

			//获取上传文件  一次只上传一个文件
			Iterator<FileItem> iter = fileItems.iterator();
			while (iter.hasNext()) {
				FileItem fi = (FileItem) iter.next();
				if (!fi.isFormField()) {
					String fileName = fi.getName();
					String fileType = "";

					int dotPos = fileName.lastIndexOf(".");
					if (dotPos > 0)
						fileType = fileName.substring(dotPos + 1);

					if (fileType == null || "".equals(fileType)
							|| (!imageTypeList.contains(fileType) && !fileTypeList.contains(fileType))) {
						fileList.clear();
						logger.info("[uploadToTempServer] unkonw file type:" + fileType);
						return Constant.UNKNOWN_FILE_TYPE;
					}
					if (fileName == null || "".equals(fileName)) {
						fileList.clear();
						return Constant.EMPTY_FILE_NAME;
					}
					//获取文件名
					fileName = UUIDHexGenerator.getInstance().generate() + "." + fileType;
					logger.debug("[uploadToTempServer] fileName:" + fileName);

					byte[] temp = fi.get();
					
					fi.write(new File(tempDir, fileName));
					File tmpFile = new File(tempDir + fileSeparator + fileName);//临时文件
					//空文件
					if (tmpFile.length() == 0) {
						tmpFile.delete();
						logger.info("[uploadToTempServer] error size, deleting all stored temp file");
						//删除所有临时文件
						for (File file : fileList) {
							file.delete();
						}
						logger.info("[uploadToTempServer] deleted.");
						fileList.clear();
						return Constant.EMPTY_FILE;
					}
					//超过大小
					if ((maxSize != null) && (tmpFile.length() > Integer.parseInt(maxSize)*1024)){
						tmpFile.delete();
						logger.info("[uploadToTempServer] file size beyond limit, deleting all stored temp file");
						//删除所有临时文件
						for (File file : fileList) {
							file.delete();
						}
						logger.info("[uploadToTempServer] deleted.");
						fileList.clear();
						return Constant.SIZE_BEYOND_LIMIT;					
					}
					
					fileList.add(tmpFile);

					String str = MD5.encode(temp);
					filePathList.add(str.substring(0, 2) + fileSeparator + str.substring(2, 4));
				}
			}
			logger.debug("[uploadToTempServer] all content stored to temp file");
			return Constant.FINISH;
		} catch (Exception e) {
			fileList.clear();
			logger.error("[uploadToTempServer] exception ocuured " + e);
			return Constant.EXCEPTION;
		}
	}

	/**
	 * 存储文件到指定路径
	 * @param request
	 * @param path
	 * @param file
	 * @param formFieldMap
	 * @return
	 */
	private String[] moveFileToPath(HttpServletRequest request, String path, File file, Map<String, String> formFieldMap) {

		Logger logger = Logger.getLogger(FileUploadServlet.class);

		String fileSeparator = File.separator;

		String fileName = file.getName();
		int dotPos = fileName.lastIndexOf(".");
		String fileType = fileName.substring(dotPos + 1);

		//依据文件名获取文件路径
		String category = "imgs";
		if (fileTypeList.contains(fileType)) {
			category = "text";
		} else if (imageTypeList.contains(fileType)) {
			category = "imgs";
		}

		logger.debug("[moveToFilePath] category is" + category);

		String[] fileUrl = new String[2];
		// set path
		String basePath = getServletContext().getRealPath("/") + fileSeparator;
		String filePathStr = category + fileSeparator + path;
		// check
		File filePath = new File(basePath + filePathStr);
		if (!filePath.isDirectory()) {
			logger.debug("[moveToFilePath] dir not exists, making...");
			filePath.mkdirs();
		}

		InputStream is = null;
		OutputStream os = null;
		try {
			is = new FileInputStream(file);
			os = new FileOutputStream(filePath + fileSeparator + fileName);

			byte[] buf = new byte[1024];
			int n;
			while ((n = is.read(buf)) != -1) {
				os.write(buf, 0, n);
			}
		} catch (IOException e) {
			logger.error("[movFileToPath]error occured when copying file to path");
		} finally {
			try {
				if (is != null) {
					is.close();
					file.delete();
				}
				if (os != null) {
					os.close();
				}
			} catch (Exception e) {
				logger.error("[movFileToPath]Failed to close file input stream");
			}

		}
		// set return info
		fileUrl[0] = (String) formFieldMap.get("fileName");
		fileUrl[1] = webBasePath + category + "/" + path.substring(0, 2) + "/" + path.substring(3) + "/" + fileName;
		logger.debug("[moveToFilePath] fileName:" + fileUrl[0] + "  filePath" + fileUrl[1]);

		return fileUrl;
	}

}
